using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Drawing2D;
using System.Drawing;

namespace AlgorithmsVisualized.Controls
{
    [Serializable]
    public class Edge : ShapeBase, IComparable<Edge>
    {
        #region DataMembers
        private double _weight;
        private Point _to;
        private Point _from;
        private double _angle;
        private Node _nodeFrom;
        private Node _nodeTo;
        private bool _isDirected;
        #endregion
        
        #region Properties
        public bool IsDirected
        {
            get { return _isDirected; }
            set { _isDirected = value; }
        }
        public Node NodeTo
        {
            get { return _nodeTo; }
            set
            {
                if (_nodeTo != null)
                    _nodeTo.PositionChanged -= new EventHandler(NodeTo_PositionChanged);
                _nodeTo = value;
                _nodeTo.PositionChanged += new EventHandler(NodeTo_PositionChanged);
                To = _nodeTo.Center;
            }
        }
        public Node NodeFrom
        {
            get { return _nodeFrom; }
            set
            {
                if (_nodeFrom != null)
                    _nodeFrom.PositionChanged -= new EventHandler(NodeFrom_PositionChanged);
                _nodeFrom = value;
                _nodeFrom.PositionChanged += new EventHandler(NodeFrom_PositionChanged);
                From = _nodeFrom.Center;
            }
        }
        public double Angle
        {
            get { return _angle; }
        }
        public Point From
        {
            get { return _from; }
            set
            {
                _from = value;
                UpdateAngle();
                OnRedrawRequest(new EventArgs());
            }
        }
        public Point To
        {
            get { return _to; }
            set
            {
                _to = value;
                //UpdateWidthAndHeight();
                UpdateAngle();
                OnRedrawRequest(new EventArgs());
            }
        }
        public double Weight
        {
            get { return _weight; }
            set { _weight = value; }
        }
        public new Point Location
        {
            get { return base.Location; }
            set
            {
                base.Location = value;
                UpdateAngle();
            }
        }
        #endregion

        /*public Edge()
        {
        }*/
        public Edge(string ID, double Weight)
        {
            _id = ID;
            _weight = Weight;
        }

        internal static Edge CreateDefault(Node From, Node To)
        {
            Edge e = new Edge(From.ID + To.ID, 0.0);
            e.BackColor = Color.White;
            e.NodeFrom = From;
            e.NodeTo = To;
            //e.Width = 50;
            //e.Height = 50;
            e.Location = new Point(0, 0);
            e.BackColor = Color.Black;
            return e;
        }

        private void UpdateWidthAndHeight()
        {
            Width = Math.Abs(To.X - Location.X);
            Height = Math.Abs(To.Y - Location.Y);
        }
        private void UpdateAngle()
        {
            //_to.Y = Height - _to.Y;
            //_from.Y = Height - _from.Y;
            Point vec = Util.Subtract(_to, _from);
            //vec.Y *= -1;
            _angle = (double)Math.Acos((double)((double)vec.X / Util.VectorLength(vec))) * 180 / Math.PI;
            //_angle = (double) Math.Atan(Math.Abs((double)(vec.Y / vec.X)));
            //if (vec.X < 0 && vec.Y > 0)
                //_angle += 90;
            if (vec.Y > 0)
                _angle = 360 - _angle;
            //if (vec.X < 0 && vec.Y < 0)
            //    _angle += 90;
            //else if (vec.X > 0 && vec.Y < 0)
            //    _angle += 270;
        }

        protected override void RefreshBitmap(Graphics g)
        {
            Point nFrom = Util.ShiftInDirection(From, NodeFrom.Width/2, To);
            Point nTo = Util.ShiftInDirection(To, NodeTo.Width / 2, From);

            if (!IsSelected)
                g.DrawLine(DrawingPen, nFrom, nTo);
            else
                g.DrawLine(SelectedDrawingPen, nFrom, nTo);
            Font f = new Font("Tahoma", 10);
            g.DrawString(Weight.ToString(), f, Brushes.Black, new PointF((From.X + To.X) / 2, (From.Y + To.Y) / 2));
            if (IsDirected)
                DrawArrowHead(g, nTo, nFrom);
            /*
            //if (Width > 0 && Height > 0)
                //RenderedImage = new Bitmap(Width, Height);
            Graphics g = Graphics.FromImage(RenderedImage);
            g.Clear(BackColor);

            GraphicsPath gp = new GraphicsPath();
            //gp.AddRectangle();
            //Rectangle rec = new Rectangle(Math.Min(From.X, To.X), Math.Min(From.Y, To.Y), Math.Abs(From.X - To.X), Math.Abs(To.Y- From.Y));
            Rectangle rec = new Rectangle(0, 0, (int)Util.VectorLength(Util.Subtract(To, From)) - NodeTo.ScaledWidth / 2 - NodeFrom.ScaledWidth / 2, 3);
            gp.AddRectangle(rec);
            Point FromP1 = new Point(From.X + 10, From.Y + 10);
            Point ToP1 = new Point(To.X + 10, To.Y + 10);
            //gp.AddPolygon(new Point[] { From, FromP1, To, ToP1, From });
            
            System.Drawing.Drawing2D.Matrix TransformationMat = new System.Drawing.Drawing2D.Matrix();
            TransformationMat.Translate(0, -1.5f);
            TransformationMat.RotateAt((float)Angle, new PointF(0, 1.5f));
            TransformationMat.Translate((float)(Math.Cos(Util.ToRadians(Angle)) * NodeFrom.ScaledWidth / 2), (float)(Math.Sin(Util.ToRadians(Angle)) * NodeFrom.ScaledWidth / 2), MatrixOrder.Append);
            TransformationMat.Translate(From.X, From.Y, MatrixOrder.Append);
            
            gp.Transform(TransformationMat);
            Region = new Region(gp);

            gp = new GraphicsPath();
            //gp.AddLine(0, 0, 50, 50);
            gp.AddLine(From.X, From.Y, To.X, To.Y);
            //gp.Transform(Matrix.GetDrawingMatrix(Matrix.Rotation(Angle)));
            if (!IsSelected)
                g.DrawPath(DrawingPen, gp);
            else
                g.DrawPath(SelectedDrawingPen, gp);
             */
        }

        private void DrawArrowHead(Graphics g, Point p1, Point p2)
        {
            GraphicsPath gp = new GraphicsPath();
            Matrix RotationTransform = new Matrix(1, 0, 0, 1, 0, 0);  // rotation matrix
            Matrix TranslationTransform = new Matrix(1, 0, 1, 0, 0, 0);  // rotation matrix
            float angle;
            try
            {
                double d = (double)(NodeFrom.Top - NodeTo.Top) / (NodeFrom.Left - NodeTo.Left);
                angle = ((float)(Math.Atan(d) * 180 / Math.PI)) + 90;
                if (NodeFrom.Left - NodeTo.Left < 0)
                    angle += 180;
            }
            catch (Exception)
            {
                angle = 0;
            }
            Point newP1 = Util.ShiftInDirection(p1, 8, p2);
            if (newP1.X >= int.MaxValue / 2 || newP1.X <= int.MinValue / 2)
                newP1.X = 0;
            if (newP1.Y >= int.MaxValue / 2 || newP1.Y <= int.MinValue / 2)
                newP1.Y = 0;
            RotationTransform.Translate(newP1.X, newP1.Y);
            RotationTransform.RotateAt(angle, new PointF(0, 0));
            gp.AddPolygon(new Point[] { new Point(-10, 0), new Point(0, 10), new Point(10, 0) });
            gp.Transform(RotationTransform);
            if (IsSelected)
                using(Brush b = new SolidBrush(SelectedDrawingPen.Color))
                    g.FillPath(b, gp);
            else
                using (Brush b = new SolidBrush(DrawingPen.Color))
                    g.FillPath(b, gp);
        }
        public override bool IsInside(System.Drawing.Point point)
        {
            return false;
        }
        #region EventHandlers
        void NodeTo_PositionChanged(object sender, EventArgs e)
        {
            To = NodeTo.Center;
        }
        void NodeFrom_PositionChanged(object sender, EventArgs e)
        {
            From = NodeFrom.Center;
        }
        #endregion


        #region IComparable<Edge> Members

        public int CompareTo(Edge other)
        {
            if (Weight.CompareTo(other.Weight) != 0)
                return Weight.CompareTo(other.Weight);
            return ID.CompareTo(other.ID);
        }

        #endregion
        public override bool Equals(object obj)
        {
            return ID == (obj as Edge).ID;
        }
    }
}
